"""
链接提取器模块

负责从网页中提取链接，并根据配置规则决定是否跟踪爬取
"""

import re
import logging
import urllib.parse
from urllib.parse import urlparse
from bs4 import BeautifulSoup

logger = logging.getLogger('link_extractor')

class BaseLinkExtractor:
    """链接提取器基类"""
    
    def __init__(self):
        """初始化链接提取器"""
        self.domains = []  # 支持的域名列表
    
    def can_extract(self, url):
        """
        检查是否可以处理该URL
        
        参数:
            url: 要检查的URL
        
        返回:
            布尔值，表示是否可以处理
        """
        if not self.domains:  # 如果没有指定域名，则支持所有域名
            return True
        
        domain = urlparse(url).netloc
        return any(domain.endswith(d) for d in self.domains)
    
    def extract_links(self, html, url):
        """
        从HTML中提取链接
        
        参数:
            html: HTML内容
            url: 当前页面URL
        
        返回:
            链接列表
        """
        raise NotImplementedError("子类必须实现extract_links方法")
    
    def should_follow(self, url):
        """
        检查是否应该跟踪该链接
        
        参数:
            url: 要检查的链接
        
        返回:
            布尔值，表示是否应该跟踪
        """
        return True


class ConfigLinkExtractor(BaseLinkExtractor):
    """基于配置的链接提取器"""
    
    def __init__(self, config):
        """
        初始化链接提取器
        
        参数:
            config: 配置对象
        """
        super().__init__()
        self.config = config
        
        # 从配置中读取设置
        self.enabled = config.getboolean('LINK_EXTRACTOR', 'enabled', fallback=True)
        self.selector = config.get('LINK_EXTRACTOR', 'selector', fallback='a[href]')
        self.follow_pattern = config.get('LINK_EXTRACTOR', 'follow_pattern', fallback=None)
        self.exclude_pattern = config.get('LINK_EXTRACTOR', 'exclude_pattern', fallback=None)
        self.same_domain_only = config.getboolean('LINK_EXTRACTOR', 'same_domain_only', fallback=True)
        self.max_links = config.getint('LINK_EXTRACTOR', 'max_links', fallback=100)
        
        # 编译正则表达式
        self.follow_regex = re.compile(self.follow_pattern) if self.follow_pattern else None
        self.exclude_regex = re.compile(self.exclude_pattern) if self.exclude_pattern else None
        
        logger.info(f"链接提取器初始化完成，启用状态: {self.enabled}")
    
    def extract_links(self, html, url):
        """
        从HTML中提取链接
        
        参数:
            html: HTML内容
            url: 当前页面URL
        
        返回:
            链接列表
        """
        if not self.enabled:
            logger.debug("链接提取器已禁用")
            return []
        
        links = []
        base_url = url
        current_domain = urlparse(url).netloc
        
        try:
            soup = BeautifulSoup(html, 'html.parser')
            
            # 查找base标签，更新base_url
            base_tag = soup.find('base', href=True)
            if base_tag:
                base_url = base_tag['href']
            
            # 提取链接
            for link in soup.select(self.selector):
                if 'href' in link.attrs:
                    href = link['href']
                    
                    # 忽略空链接和锚点链接
                    if not href or href.startswith('#') or href.startswith('javascript:'):
                        continue
                    
                    # 构建绝对URL
                    abs_url = urllib.parse.urljoin(base_url, href)
                    
                    # 检查是否应该跟踪
                    if self.should_follow(abs_url, current_domain):
                        links.append(abs_url)
                    
                    # 如果达到最大链接数，停止提取
                    if len(links) >= self.max_links:
                        logger.debug(f"已达到最大链接数 {self.max_links}")
                        break
            
            logger.info(f"从 {url} 提取了 {len(links)} 个链接")
            return links
            
        except Exception as e:
            logger.error(f"提取链接时出错: {e}")
            return []
    
    def should_follow(self, url, current_domain=None):
        """
        检查是否应该跟踪该链接
        
        参数:
            url: 要检查的链接
            current_domain: 当前页面的域名
        
        返回:
            布尔值，表示是否应该跟踪
        """
        # 检查是否是有效URL
        try:
            parsed = urlparse(url)
            if not parsed.scheme or not parsed.netloc:
                return False
        except:
            return False
        
        # 检查是否只跟踪同域名
        if self.same_domain_only and current_domain:
            if urlparse(url).netloc != current_domain:
                return False
        
        # 检查排除模式
        if self.exclude_regex and self.exclude_regex.search(url):
            return False
        
        # 检查跟踪模式
        if self.follow_regex:
            return bool(self.follow_regex.search(url))
        
        return True


class LinkExtractorManager:
    """链接提取器管理器"""
    
    def __init__(self, config):
        """
        初始化链接提取器管理器
        
        参数:
            config: 配置对象
        """
        self.config = config
        self.extractors = []
        
        # 添加基于配置的链接提取器
        self.config_extractor = ConfigLinkExtractor(config)
        self.extractors.append(self.config_extractor)
        
        # 加载自定义链接提取器插件
        self._load_plugins()
        
        logger.info(f"链接提取器管理器初始化完成，共加载 {len(self.extractors)} 个提取器")
    
    def _load_plugins(self):
        """加载自定义链接提取器插件"""
        # TODO: 实现插件加载逻辑
        pass
    
    def get_extractor(self, url):
        """
        为URL获取合适的链接提取器
        
        参数:
            url: 要提取链接的URL
        
        返回:
            链接提取器对象或None
        """
        # 先检查自定义提取器
        for extractor in self.extractors:
            if extractor is not self.config_extractor and extractor.can_extract(url):
                logger.info(f"使用自定义链接提取器: {extractor.__class__.__name__} 处理 {url}")
                return extractor
        
        # 然后使用基于配置的提取器
        if self.config_extractor.enabled:
            logger.info(f"使用配置链接提取器处理 {url}")
            return self.config_extractor
        
        logger.warning(f"未找到合适的链接提取器: {url}")
        return None
    
    def extract_links(self, html, url):
        """
        从HTML中提取链接
        
        参数:
            html: HTML内容
            url: 当前页面URL
        
        返回:
            链接列表
        """
        extractor = self.get_extractor(url)
        if extractor:
            return extractor.extract_links(html, url)
        return []
